feat(runtime): execute canonical agent specs in deterministic dry-run#6
feat(runtime): execute canonical agent specs in deterministic dry-run#6ProfRandom92 wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Code Review
This pull request adds agent-related commands (validate-spec, dry-run, and replay) to the CLI, introducing new modules for contract definitions and agent execution logic, alongside a security check to block access to sensitive directories. The feedback highlights several critical improvements: preventing a potential panic when replaying empty evidence logs, using case-insensitive comparisons to avoid security bypasses on case-insensitive file systems, buffering file writes to improve performance, and caching compiled regular expressions using OnceLock.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| if replay.evidence_root_hash != actual_events.last().unwrap().event_hash { | ||
| emit_p1_error( | ||
| "REPLAY_VERIFICATION_FAILED", | ||
| &format!("Root hash mismatch: manifest={}, final_event={}", replay.evidence_root_hash, actual_events.last().unwrap().event_hash), | ||
| None | ||
| ); | ||
| return Ok(1); | ||
| } |
There was a problem hiding this comment.
If the evidence log is empty (or both the replay manifest and evidence log are empty), calling actual_events.last().unwrap() will panic and crash the CLI instead of returning a structured error. We should safely handle the case where actual_events is empty by using last() with safe mapping or fallback.
| if replay.evidence_root_hash != actual_events.last().unwrap().event_hash { | |
| emit_p1_error( | |
| "REPLAY_VERIFICATION_FAILED", | |
| &format!("Root hash mismatch: manifest={}, final_event={}", replay.evidence_root_hash, actual_events.last().unwrap().event_hash), | |
| None | |
| ); | |
| return Ok(1); | |
| } | |
| let last_event_hash = actual_events.last().map(|e| e.event_hash.as_str()).unwrap_or(""); | |
| if replay.evidence_root_hash != last_event_hash { | |
| emit_p1_error( | |
| "REPLAY_VERIFICATION_FAILED", | |
| &format!("Root hash mismatch: manifest={}, final_event={}", replay.evidence_root_hash, last_event_hash), | |
| None | |
| ); | |
| return Ok(1); | |
| } |
| for component in path.components() { | ||
| if let std::path::Component::Normal(os_str) = component { | ||
| if let Some(s) = os_str.to_str() { | ||
| if s == ".git" || s == ".ssh" || s == ".aws" { |
There was a problem hiding this comment.
On case-insensitive file systems (such as Windows or macOS), checking for exact matches like s == ".git" can be bypassed by using different casing (e.g., .GIT or .Ssh). Use eq_ignore_ascii_case to ensure the security policy cannot be bypassed via casing.
| if s == ".git" || s == ".ssh" || s == ".aws" { | |
| if s.eq_ignore_ascii_case(".git") || s.eq_ignore_ascii_case(".ssh") || s.eq_ignore_ascii_case(".aws") { |
| fn write_events_to_file(events: &[EvidenceEvent], file_path: &str) -> Result<(), String> { | ||
| let mut file = File::create(file_path) | ||
| .map_err(|e| format!("Failed to create evidence file: {e}"))?; | ||
| for e in events { | ||
| let serialized = serde_json::to_string(e).unwrap(); | ||
| writeln!(file, "{}", serialized) | ||
| .map_err(|e| format!("Failed to write to evidence file: {e}"))?; | ||
| } | ||
| Ok(()) | ||
| } |
There was a problem hiding this comment.
Writing to a file line-by-line using writeln!(file, ...) without buffering performs a system call for every single line, which is highly inefficient. Wrapping the file in a BufWriter will buffer the writes and significantly improve performance.
fn write_events_to_file(events: &[EvidenceEvent], file_path: &str) -> Result<(), String> {
let file = File::create(file_path)
.map_err(|e| format!("Failed to create evidence file: {e}"))?;
let mut writer = std::io::BufWriter::new(file);
for e in events {
let serialized = serde_json::to_string(e).unwrap();
writeln!(writer, "{}", serialized)
.map_err(|e| format!("Failed to write to evidence file: {e}"))?;
}
writer.flush().map_err(|e| format!("Failed to flush evidence file: {e}"))?;
Ok(())
}| } | ||
|
|
||
| // Check regex: ^[a-z][a-z0-9_-]*$ | ||
| let re = regex::Regex::new(r"^[a-z][a-z0-9_-]*$").unwrap_or_else(|_| regex::Regex::new(".*").unwrap()); |
There was a problem hiding this comment.
Compiling the regular expression ^[a-z][a-z0-9_-]*$ on every invocation of handle_agent_validate_spec is inefficient. Since the pattern is static, we can compile it once using std::sync::OnceLock to reuse the compiled regex across calls.
static RE: std::sync::OnceLock<regex::Regex> = std::sync::OnceLock::new();
let re = RE.get_or_init(|| regex::Regex::new(r"^[a-z][a-z0-9_-]*$").unwrap());
Objective
Implement executable dry-run and replay validation within the Rust runtime CLI.
Contract authority
The runtime depends on the canonical AIR schemas from
comptext-airand implements native Rust types to mirror these structures.Architecture
src/contracts.rsdefining deserializable JSON types forAgentSpec,EvidenceEvent,CompletionContract,ReplayManifest, andErrorEnvelope.src/cli_p1.rsimplementing CLI commands:ctxt agent validate-spec <spec-path>ctxt agent dry-run --spec <spec-path> --out-evidence <evidence-path> --out-replay <replay-path>ctxt agent replay --replay <replay-path> --evidence <evidence-path>Changes
Cargo.tomlandsrc/main.rs.src/contracts.rsandsrc/cli_p1.rs.ErrorEnvelopeformat on failures.Determinism guarantees
JCS sorted key serialization guarantees identical SHA-256 hashes on identical structures across platforms.
Capability security
Capability policy enforcement evaluates inputs and defaults to
DENYfor any unknown tools.Evidence and replay
Monotonic sequence checks and back-linked SHA-256 parent hashes ensure evidence chain-of-custody.
Compatibility
Fully backward-compatible with existing 83 integration tests and 38 unit tests.
Tests
src/cli_p1.rstesting dry-run events sequence, root hash determinism, and mutation rejections.jsonschemalibrary from the Cargo test runner.CI
Local tests are 100% green.
Known limitations
None.
Rollback
Git revert on merge commit.
Evidence